use super::{const_buf, const_void, mut_buf, mut_void, AioFd, Fd, SocketAddr};
use crate::event::{POLLIN, POLLOUT};
use crate::Error;
use core::mem;
use core::time::Duration;
use hierr::set_errno;

pub const SHUT_RD: i32 = libc::SHUT_RD;
pub const SHUT_WR: i32 = libc::SHUT_WR;
pub const SHUT_RDWR: i32 = libc::SHUT_RDWR;

impl Fd {
    pub fn socket(family: i32, ty: i32, proto: i32) -> Result<Self, Error> {
        let fd =
            unsafe { libc::socket(family, ty | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC, proto) };
        if fd >= 0 {
            Ok(Self::new(fd))
        } else {
            Err(Error::last_error())
        }
    }

    pub fn shutdown(&self, how: i32) -> Result<(), Error> {
        if unsafe { libc::shutdown(self.fd, how) } == 0 {
            return Ok(());
        }
        Err(Error::last_error())
    }

    pub fn tcp_server(addr: &SocketAddr) -> Result<Self, Error> {
        let (addr, addrlen) = addr.get();
        let fd = Self::socket(addr.sa_family.into(), libc::SOCK_STREAM, 0)?;
        fd.tcp_server_config();
        let ret = unsafe { libc::bind(fd.fd, addr, addrlen) };
        if ret != 0 {
            return Err(Error::last_error());
        }

        let ret = unsafe { libc::listen(fd.fd, libc::SOMAXCONN) };
        if ret != 0 {
            return Err(Error::last_error());
        }

        Ok(fd)
    }

    pub fn tcp_client(family: i32, addr: Option<&SocketAddr>) -> Result<Self, Error> {
        let fd = Self::socket(family, libc::SOCK_STREAM, 0)?;
        if let Some(addr) = addr {
            let (addr, addrlen) = addr.get();
            let ret = unsafe { libc::bind(fd.fd, addr, addrlen) };
            if ret != 0 {
                return Err(Error::last_error());
            }
        }
        Ok(fd)
    }

    pub fn try_connect(&self, addr: &SocketAddr) -> Result<(), Error> {
        loop {
            let (addr, addrlen) = addr.get();
            let ret = unsafe { libc::connect(self.fd, addr, addrlen) };
            if ret == 0 {
                return Ok(());
            }
            let err = Error::last_error();
            if err.errno == libc::EINTR {
                continue;
            }
            return Err(err);
        }
    }

    pub fn set_linger(&self, time: Option<Duration>) {
        let linger = if let Some(tm) = time {
            libc::linger {
                l_onoff: 1,
                l_linger: tm.as_secs() as i32,
            }
        } else {
            libc::linger {
                l_onoff: 0,
                l_linger: 0,
            }
        };
        unsafe {
            libc::setsockopt(
                self.fd,
                libc::SOL_SOCKET,
                libc::SO_LINGER,
                const_void(&linger),
                mem::size_of_val(&linger) as u32,
            );
        }
    }

    pub fn getsockname(&self) -> Result<SocketAddr, Error> {
        let mut sockaddr = SocketAddr::uninit();
        let (addr, mut addrlen) = sockaddr.get_uninit_mut();
        let ret = unsafe { libc::getsockname(self.fd, addr, &mut addrlen) };
        if ret == 0 {
            return Ok(sockaddr);
        }
        Err(Error::last_error())
    }

    pub fn getpeername(&self) -> Result<SocketAddr, Error> {
        let mut sockaddr = SocketAddr::uninit();
        let (addr, mut addrlen) = sockaddr.get_uninit_mut();
        let ret = unsafe { libc::getpeername(self.fd, addr, &mut addrlen) };
        if ret == 0 {
            return Ok(sockaddr);
        }
        Err(Error::last_error())
    }

    pub fn try_sendto(&self, buf: &[u8], flags: i32, dst: &SocketAddr) -> Result<usize, Error> {
        let flags = flags | libc::MSG_DONTWAIT;
        let (addr, addrlen) = dst.get();
        let ret = unsafe { libc::sendto(self.fd, const_buf(buf), buf.len(), flags, addr, addrlen) };
        if ret >= 0 {
            return Ok(ret as usize);
        }
        Err(Error::last_error())
    }

    pub fn try_recvfrom(&self, buf: &mut [u8], flags: i32) -> Result<(usize, SocketAddr), Error> {
        let flags = flags | libc::MSG_DONTWAIT;
        let mut sockaddr = SocketAddr::uninit();
        let (addr, mut addrlen) = sockaddr.get_uninit_mut();
        let ret =
            unsafe { libc::recvfrom(self.fd, mut_buf(buf), buf.len(), flags, addr, &mut addrlen) };
        if ret >= 0 {
            return Ok((ret as usize, sockaddr));
        }
        Err(Error::last_error())
    }

    fn tcp_server_config(&self) {
        let val = 1_i32;
        unsafe {
            libc::setsockopt(
                self.fd,
                libc::SOL_SOCKET,
                libc::SO_REUSEADDR,
                const_void(&val),
                mem::size_of_val(&val) as u32,
            );
        }
    }

    fn tcp_connection_config(&self) {}
}

impl AioFd<'_> {
    pub async fn accept(&mut self) -> Result<(Fd, SocketAddr), Error> {
        let mut peer_addr = SocketAddr::uninit();
        loop {
            // 确保addr不能跨await
            {
                let (addr, mut addrlen) = peer_addr.get_uninit_mut();
                let ret = unsafe {
                    libc::accept4(
                        self.fd.fd,
                        addr,
                        &mut addrlen,
                        libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC,
                    )
                };
                if ret >= 0 {
                    let conn = Fd::new(ret);
                    conn.tcp_connection_config();
                    return Ok((conn, peer_addr));
                }
            }
            let err = Error::last_error();
            match err.errno {
                libc::EINTR => continue,
                libc::EAGAIN => {
                    self.wait(POLLIN).await?;
                    continue;
                }
                _ => return Err(err),
            }
        }
    }

    pub async fn connect(&mut self, addr: &SocketAddr) -> Result<(), Error> {
        let (addr, addrlen) = addr.get();
        loop {
            let ret = unsafe { libc::connect(self.fd.fd, addr, addrlen) };
            if ret == 0 {
                return Ok(());
            }
            let e = Error::last_error();
            if e.errno == libc::EINPROGRESS || e.errno == libc::EALREADY {
                self.wait(POLLOUT).await?;
                let mut val: i32 = 0;
                let mut len = mem::size_of_val(&val) as u32;
                let ret = unsafe {
                    libc::getsockopt(
                        self.fd.fd,
                        libc::SOL_SOCKET,
                        libc::SO_ERROR,
                        mut_void(&mut val),
                        &mut len,
                    )
                };
                if ret == 0 {
                    if val == 0 {
                        return Ok(());
                    }
                    set_errno(val);
                    return Err(Error::new(val));
                }
                return Err(Error::last_error());
            }
        }
    }
}
